use crate::response::Response;
use axum::{
    http::StatusCode,
    response::{IntoResponse, Response as AxumResponse},
    Json,
};
use std::backtrace::Backtrace;
use std::fmt;

/// 应用错误 - 极简设计
///
/// 设计理念:
/// 1. 单一类型,不区分错误类别
/// 2. 错误码可自定义,默认 500
/// 3. 自动捕获并过滤堆栈
/// 4. 支持错误链(通过 source)
#[derive(Debug)]
pub struct AppError {
    /// 错误码(默认 500)
    pub code: i32,
    /// 错误消息(给用户看的)
    pub message: String,
    /// 原始错误(可选,用于错误链)
    pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
    /// 堆栈信息
    backtrace: Backtrace,
}

impl AppError {
    /// 创建一个新错误(默认错误码 500)
    pub fn new(message: impl Into<String>) -> Self {
        Self {
            code: 500,
            message: message.into(),
            source: None,
            backtrace: Backtrace::capture(),
        }
    }

    /// 创建带错误码的错误
    pub fn with_code(code: i32, message: impl Into<String>) -> Self {
        Self {
            code,
            message: message.into(),
            source: None,
            backtrace: Backtrace::capture(),
        }
    }

    /// 从其他错误转换,保留错误链
    pub fn from_error<E>(err: E) -> Self
    where
        E: std::error::Error + Send + Sync + 'static,
    {
        Self {
            code: 500,
            message: err.to_string(),
            source: Some(Box::new(err)),
            backtrace: Backtrace::capture(),
        }
    }

    /// 从其他错误转换,并指定错误码
    pub fn from_error_with_code<E>(code: i32, err: E) -> Self
    where
        E: std::error::Error + Send + Sync + 'static,
    {
        Self {
            code,
            message: err.to_string(),
            source: Some(Box::new(err)),
            backtrace: Backtrace::capture(),
        }
    }

    /// 添加上下文信息(类似 anyhow 的 context)
    pub fn context(mut self, ctx: impl Into<String>) -> Self {
        let ctx = ctx.into();
        self.message = format!("{}: {}", ctx, self.message);
        self
    }

    /// 获取过滤后的堆栈信息
    pub fn filtered_backtrace(&self) -> String {
        let backtrace_str = format!("{}", self.backtrace);

        if std::env::var("FULL_BACKTRACE").is_ok() {
            return backtrace_str;
        }

        let lines: Vec<&str> = backtrace_str.lines().collect();
        let mut indices = Vec::new();

        for (idx, line) in lines.iter().enumerate() {
            let trimmed = line.trim();

            if !trimmed.starts_with("at ") {
                continue;
            }

            let normalized = trimmed.replace('\\', "/").to_lowercase();
            let is_src_path = normalized.contains("/src/");
            let is_dependency_path = normalized.contains("/.cargo/")
                || normalized.contains("/.rustup/")
                || normalized.contains("/rustc/")
                || normalized.contains("/target/");

            if is_src_path && !is_dependency_path {
                if idx > 0 {
                    let prev_trimmed = lines[idx - 1].trim();
                    if !prev_trimmed.starts_with("at ") {
                        indices.push(idx - 1);
                    }
                }
                indices.push(idx);
            }
        }

        indices.sort_unstable();
        indices.dedup();

        let filtered: Vec<&str> = indices.into_iter().map(|i| lines[i]).collect();

        if filtered.is_empty() {
            "堆栈信息已过滤(设置环境变量 FULL_BACKTRACE=1 查看完整堆栈)".to_string()
        } else {
            format!(
                "堆栈信息(已过滤):\n{}\n\n提示: 设置环境变量 FULL_BACKTRACE=1 查看完整堆栈",
                filtered.join("\n")
            )
        }
    }

    /// 获取详细错误信息(包含错误链和堆栈)
    pub fn detail(&self) -> String {
        let mut parts = Vec::new();

        // 1. 主错误消息
        parts.push(format!("错误: {}", self.message));

        // 2. 错误链
        if let Some(source) = &self.source {
            parts.push("\n错误链:".to_string());

            let mut current: &(dyn std::error::Error + 'static) = source.as_ref();
            let mut level = 1;

            parts.push(format!("  [{}] {}", level, current));

            while let Some(next) = current.source() {
                level += 1;
                parts.push(format!("  [{}] {}", level, next));
                current = next;
            }
        }

        // 3. 堆栈信息(过滤后)
        parts.push(format!("\n{}", self.filtered_backtrace()));

        parts.join("\n")
    }
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl std::error::Error for AppError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.source.as_ref().map(|e| e.as_ref() as _)
    }
}

// 实现 IntoResponse,让 AppError 可以直接作为 handler 返回值
impl IntoResponse for AppError {
    fn into_response(self) -> AxumResponse {
        let code = self.code;
        let msg = self.message.clone();
        let detail = self.detail();

        tracing::error!(
            error_code = code,
            error_msg = %msg,
            error_detail = %detail,
            "请求处理失败"
        );

        let response: Response<()> = Response::failed_with_code(code, msg, Some(detail));

        (StatusCode::OK, Json(response)).into_response()
    }
}

impl From<std::io::Error> for AppError {
    fn from(err: std::io::Error) -> Self {
        Self::from_error(err)
    }
}

impl From<serde_json::Error> for AppError {
    fn from(err: serde_json::Error) -> Self {
        Self::from_error_with_code(400, err)
    }
}

impl From<std::num::ParseIntError> for AppError {
    fn from(err: std::num::ParseIntError) -> Self {
        Self::from_error_with_code(400, err)
    }
}

impl From<std::num::ParseFloatError> for AppError {
    fn from(err: std::num::ParseFloatError) -> Self {
        Self::from_error_with_code(400, err)
    }
}

#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for AppError {
    fn from(err: anyhow::Error) -> Self {
        Self {
            code: 500,
            message: err.to_string(),
            source: None,
            backtrace: Backtrace::capture(),
        }
    }
}

/// 为 Result 添加 context 方法(类似 anyhow)
pub trait ResultExt<T> {
    fn context(self, ctx: impl Into<String>) -> Result<T, AppError>;
    fn with_code(self, code: i32) -> Result<T, AppError>;
}

impl<T, E> ResultExt<T> for Result<T, E>
where
    E: std::error::Error + Send + Sync + 'static,
{
    fn context(self, ctx: impl Into<String>) -> Result<T, AppError> {
        self.map_err(|e| AppError::from_error(e).context(ctx))
    }

    fn with_code(self, code: i32) -> Result<T, AppError> {
        self.map_err(|e| AppError::from_error_with_code(code, e))
    }
}

/// 为任意 Result 添加上下文的简化方法
pub trait ContextExt<T, E> {
    fn c(self, ctx: &str) -> Result<T, AppError>;
}

impl<T, E> ContextExt<T, E> for Result<T, E>
where
    E: std::error::Error + Send + Sync + 'static,
{
    fn c(self, ctx: &str) -> Result<T, AppError> {
        self.map_err(|e| AppError::from_error(e).context(ctx))
    }
}
